package com.leyou.item.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.leyou.common.constant.MQConstants;
import com.leyou.common.exception.LyException;
import com.leyou.common.exception.enums.ExceptionEnum;
import com.leyou.common.pojo.PageResult;
import com.leyou.common.utils.BeanHelper;
import com.leyou.item.dto.BrandDTO;
import com.leyou.item.dto.CategoryDTO;
import com.leyou.item.dto.SkuDTO;
import com.leyou.item.dto.SpuDTO;
import com.leyou.item.entity.*;
import com.leyou.item.mapper.SkuMapper;
import com.leyou.item.mapper.SpuDetailMapper;
import com.leyou.item.mapper.SpuMapper;
import com.sun.org.apache.regexp.internal.RE;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author LT
 * @version 1.0.0
 * @ClassName GoodsService.java
 * @Description TODO Goods商品的业务层
 * @createTime 2020年05月06日 23:35:00
 * <p>
 * (propagation = Propagation.REQUIRED)
 * 事务的传播行为中，REQUIRED表示，当前service如果有事务，
 * 那么当前service中调用的其他所有service，都不再开启新事务，直接使用当前service的事务
 * 如果当前service没有事务，那么就默认使用第一个调用的service的事务
 */
@Slf4j
@Service
@Transactional
public class GoodsService {

    @Autowired
    private SpuMapper spuMapper;

    @Autowired
    private SpuDetailMapper detailMapper;

    @Autowired
    private SkuMapper skuMapper;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private BrandService brandService;

    @Autowired
    private AmqpTemplate amqpTemplate;

    /**
     * 根据条件分页查询商品：spu
     *
     * @param key      搜索条件
     * @param saleable 是否上下架
     * @param page     当前页
     * @param rows     页大小
     * @return 当前页的分页数据
     */
    public PageResult<SpuDTO> findSpuByPage(Integer page, Integer rows, String key, Boolean saleable) {
        // 1.1 提供分页信息
        PageHelper.startPage(page, rows);
        // 1.2 创建一个封装复制条件的对象
        Example example = new Example(Spu.class);
        Example.Criteria criteria = example.createCriteria();
        // 1.3 封装复杂条件
        // if(StringUtils.isNotBlank(key)){
        //     criteria.andLike("name", "%" + key + "%");
        // }
        if (!StringUtils.isBlank(key)) {
            criteria.andLike("name", "%" + key + "%");
        }
        /**
         * 注意mysql数据中tinyint(1)表示布尔值
         * java向改字段传true，则自动保存为1，传false则自动保存为0
         * 该字段如果是0，java可以直接用false获取，非0就是true
         */
        // 2.1 封装是否上下架
        if (saleable != null) {
            criteria.andEqualTo("saleable", saleable);
        }
        // 2.2 数据库查询
        List<Spu> spuList = spuMapper.selectByExample(example);
        // 判空
        if (CollectionUtils.isEmpty(spuList)) {
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        // 3. 封装一个PageHelper的分页对象
        PageInfo<Spu> pageInfo = new PageInfo<>(spuList);
        // 3.1 总页数
        int totalPages = pageInfo.getPages();
        // 3.2 总记录数
        long total = pageInfo.getTotal();
        // 3.3 当前页数据
        List<Spu> list = pageInfo.getList();
        // 3.4 转换DTO:此时，分类和品牌只有Id，还没有他们的名称
        List<SpuDTO> spuDTOS = BeanHelper.copyWithCollection(list, SpuDTO.class);
        // 3.5 把分类和品牌的名称处理
        handlerCategoryNameAndBradName(spuDTOS);
        // 4. 封装分页对象并返回
        return new PageResult<SpuDTO>(
                total,
                totalPages,
                spuDTOS
        );
    }

    /**
     * 把分类的名称和品牌的名称补全
     *
     * @param spuDTOS spu的列表数据
     */
    private void handlerCategoryNameAndBradName(List<SpuDTO> spuDTOS) {
        for (SpuDTO spuDTO : spuDTOS) {
            // 品牌名称
            Long brandId = spuDTO.getBrandId();
            BrandDTO brandDTO = brandService.findBrandById(brandId);
            spuDTO.setBrandName(brandDTO.getName());
            // 分类名称
            List<Long> categoryIds = spuDTO.getCategoryIds();
            List<CategoryDTO> categoryDTOList = categoryService.findCategoryListByIds(categoryIds);
            String categoryNames = categoryDTOList.stream()
                    .map(CategoryDTO::getName)
                    .collect(Collectors.joining("/"));
            spuDTO.setCategoryName(categoryNames);
        }
    }

    /**
     * 保存商品
     *
     * @param spuDTO
     * @return
     */
    public void saveGoods(SpuDTO spuDTO) {
        // 1、保存 Spu
        // 1.1 先把DTO对象转成PO对象
        Spu spu = BeanHelper.copyProperties(spuDTO, Spu.class);
        spu.setSaleable(false);// 设置为下架
        spu.setCreateTime(new Date());// 创建时间
        // 1.2 保存Spu
        int count = spuMapper.insertSelective(spu);//保存非空的数据
        if (count != 1) {
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }
        // 1.3 当我们插入了spu，spu对象中有一个@KeySql注解，这个注解可以帮我们自动回填id
        Long spuId = spu.getId();


        // 2、保存 SpuDetail
        SpuDetail spuDetail = spuDTO.getSpuDetail();
        spuDetail.setSpuId(spuId); // spu的id
        count = detailMapper.insertSelective(spuDetail);
        if (count != 1) {
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }
        // 3、保存Sku列表
        List<Sku> skus = spuDTO.getSkus();
        // 3.1 设置spu的id
        skus.forEach(s -> {
            s.setSpuId(spuId); // spu的ID
            s.setCreateTime(new Date());
        });
        // 3.2 保存sku列表
        count = skuMapper.insertList(skus); // 批量保存
        if (count != skus.size()) {
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }
    }
    // public void saveGoods(SpuDTO spuDTO) {
    //     try {
    //         // 获取Spu对象
    //         Spu spu = BeanHelper.copyProperties(spuDTO, Spu.class);
    //         // 设置商品为下架
    //         spu.setSaleable(false);
    //         // 保存Spu
    //         spuMapper.insertSelective(spu);
    //         // 获取SpuDetail对象
    //         SpuDetail spuDetail = spuDTO.getSpuDetail();
    //         // 给主键赋值
    //         spuDetail.setSpuId(spu.getId());
    //         // 保存SpuDetail
    //         detailMapper.insertSelective(spuDetail);
    //
    //         // 获取sku集合
    //         List<Sku> skuList = spuDTO.getSkus();
    //         // 对sku集合中的元素进行处理
    //         skuList.forEach(sku -> {
    //             // 给外键设置值
    //             sku.setSpuId(spu.getId());
    //             // 给保存时间设置值
    //             sku.setCreateTime(new Date());
    //             // 给更新时间设置值
    //             sku.setUpdateTime(new Date());
    //         });
    //         // 保存sku集合
    //         skuMapper.insertList(skuList);
    //     } catch (Exception e) {
    //         throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
    //     }
    // }

    /**
     * 商品上下架操作
     *
     * @param id       商品id
     * @param saleable 是否上下架： ture=上架，false=下架
     * @return 在修改spu的上下架状态时，无需修改sku的enable属性，sku的enable属性只与当前sku的库存量有关，如果库存大于0，就是有效，否则是无效。
     * updateByPrimaryKeySelective ： 根据主键更新对象中非空的字段
     * update tb_spu set saleable - ? where id = ？
     */
    public void updateSaleable(Long id, Boolean saleable) {
        //凡是根据id做条件的修改都是简单修改，否则就是要用Example对象来封装复杂修改
        try {
            Spu record = new Spu();
            record.setId(id);
            record.setSaleable(saleable);
            spuMapper.updateByPrimaryKeySelective(record);
        } catch (Exception e) {
            throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
        }
        // 发送Mq消息，生成或者删除，商品的索引和静态页
        amqpTemplate.convertAndSend(
                MQConstants.Exchange.ITEM_EXCHANGE_NAME, // 交换机的名称
                (saleable ? MQConstants.RoutingKey.ITEM_UP_KEY : MQConstants.RoutingKey.ITEM_DOWN_KEY), // 路由Key
                id // 发送商品的id
        );
    }

    /**
     * 根据Spuld查询Sku对象集合
     *
     * @param id
     * @return
     */
    public List<Sku> findSkusBySpuId(Long spuId) {
        // 拼装条件查询
        Sku sku = new Sku();
        sku.setSpuId(spuId);
        List<Sku> skuList = skuMapper.select(sku);
        // 判空
        if (CollectionUtils.isEmpty(skuList)) {
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        return skuList;
    }

    /**
     * 根据SpuId查询SpuDetail对象
     *
     * @param id
     * @return
     */
    public SpuDetail findSpuDetailById(Long id) {
        SpuDetail spuDetail = detailMapper.selectByPrimaryKey(id);
        if (spuDetail == null) {
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        return spuDetail;
    }


    /**
     * 根据id查询spu：  返回的对象包含了SpuDetail和Sku列表
     *
     * @param spuId
     * @return
     */
    public SpuDTO findSpuById(Long spuId) {
        // 1、先查询spu
        Spu spu = spuMapper.selectByPrimaryKey(spuId);
        if (spu == null) {
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        // 2、转成DTO
        SpuDTO spuDTO = BeanHelper.copyProperties(spu, SpuDTO.class);
        // 3、查询SpuDetail
        SpuDetail spuDetail = findSpuDetailById(spuId);
        spuDTO.setSpuDetail(spuDetail);
        // 4、查询Sku列表
        List<Sku> skuList = findSkusBySpuId(spuId);
        spuDTO.setSkus(skuList);
        // 5、返回
        return spuDTO;
    }

    /**
     * 根据SpuId查询SpuDetail对象(白给)
     *
     * @param id
     * @return
     */
    // public SpuDetail querySpuDetailSpuId(Long id) {
    //     SpuDetail spuDetail = detailMapper.selectByPrimaryKey(id);
    //     if (spuDetail == null) {
    //         throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
    //     }
    //     return BeanHelper.copyProperties(spuDetail, SpuDetail.class);
    // }

    /**
     * 根据Spuld查询Sku对象集合(白给)
     *
     * @param id
     * @return
     */
    // public List<Sku> querySkuListBySpuId(Long id) {
    //     Sku record = new Sku();
    //     record.setSpuId(id);
    //     List<Sku> skuList = skuMapper.select(record);
    //     if (CollectionUtils.isEmpty(skuList)) {
    //         throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
    //     }
    //     return BeanHelper.copyWithCollection(skuList, Sku.class);
    // }

    /**
     * 根据id批量查询sku
     *
     * @param ids skuId的集合
     * @return sku的集合
     */
    public List<Sku> findSkusByIds(List<Long> ids) {
        // 批量查询
        List<Sku> skuList = skuMapper.selectByIdList(ids);
        // 判空
        if (CollectionUtils.isEmpty(skuList)) {
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        // 返回
        return skuList;
    }
      

    /**
     * 修改商品
     * TODO spu数据可以修改，但是SKU数据无法修改，因为有可能之前存在的SKU现在已经不存在了，或者以前的sku属性都不存在了。比如以前内存有4G，现在没了。
     * TODO 因此这里直接删除以前的SKU，然后新增即可
     *
     * @param spuDTO
     * @return
     */
    // @Transactional  // 事务控制
    public void updateGoods(SpuDTO spuDTO) {  // 飞飞修改
        Long spuId = spuDTO.getId();
        if (spuId == null) {
            throw new LyException(ExceptionEnum.INVALID_PARAM_ERROR);
        }

        // 1.修改skus
        Sku sku = new Sku();
        // 先根据spuId删除skus
        sku.setSpuId(spuId);
        int size = skuMapper.selectCount(sku);
        int count = 0;
        if (size > 0) {
            count = skuMapper.delete(sku);
            if (count != size) {
                throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
            }
        }

        // 2.修改spu（修改tb_spu表的数据）
        Spu spu = BeanHelper.copyProperties(spuDTO, Spu.class);
        spu.setSaleable(false);
        spu.setCreateTime(new Date());
        spu.setUpdateTime(new Date());
        count = spuMapper.updateByPrimaryKey(spu);
        if (count != 1) {
            throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
        }

        // 3.修改spuDetail （修改tb_spu_detail表的数据）
        SpuDetail spuDetail = spuDTO.getSpuDetail();
        spuDetail.setSpuId(spuId);
        spuDetail.setCreateTime(spu.getCreateTime());
        spuDetail.setUpdateTime(spu.getUpdateTime());
        count = detailMapper.updateByPrimaryKeySelective(spuDetail);
        if (count != 1) {
            throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
        }

        // 4.再重新插入skus （修改tb_sku表的数据）
        List<Sku> skuList = spuDTO.getSkus();
        skuList.forEach(s -> {
            s.setCreateTime(new Date());
            s.setUpdateTime(new Date());
            s.setSpuId(spuId);
        });
        count = skuMapper.insertList(skuList);
        if (count != skuList.size()) {
            throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
        }
    }


    // 这更新业务有主键重复错误
    // public void updateGoods(SpuDTO spuDTO) {
    //     // 查询spuId是否为null
    //     Long spuDTOId = spuDTO.getId();
    //     // 判空
    //     if (spuDTOId == null) {
    //         throw new LyException(ExceptionEnum.INVALID_PARAM_ERROR);// 请求参数有误
    //     }
    //
    //     // 1、删除sku
    //     // 先根据spuDTOId删除skus
    //     Sku sku = new Sku();
    //     // Sku sku = BeanHelper.copyProperties(spuDTO, Sku.class);
    //     sku.setSpuId(spuDTOId);
    //     // 先查询数量
    //     int size = skuMapper.selectCount(sku);
    //     if (size > 0) {
    //         int count = skuMapper.delete(sku);// 删除
    //         if (size != count) {
    //             throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
    //         }
    //     }
    //
    //     // 2、更新spu
    //     Spu spu = BeanHelper.copyProperties(spuDTO, Spu.class);
    //     spu.setSaleable(false);
    //     spu.setCreateTime(new Date());
    //     spu.setUpdateTime(new Date());
    //     // 2.1 更新spu
    //     int count = spuMapper.insertSelective(spu);
    //     if (count != 1) {
    //         throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
    //     }
    //
    //     // 3、更新SpuDetail
    //     SpuDetail spuDetail = spuDTO.getSpuDetail();
    //     spuDetail.setSpuId(spuDTOId);
    //     spuDetail.setUpdateTime(null);
    //     spuDetail.setCreateTime(null);
    //     count = detailMapper.insertSelective(spuDetail);
    //     if (count != 1) {
    //         throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
    //     }
    //
    //     // 4、新增sku集合
    //     List<Sku> skus = spuDTO.getSkus();
    //     skus.forEach(s -> {
    //         s.setCreateTime(new Date());
    //         s.setUpdateTime(new Date());
    //         s.setSpuId(spuDTOId);  // spu的id
    //     });
    //     // 5、保存sku列表 (批量插入)
    //     count = skuMapper.insertList(skus);
    //     if (count != skus.size()) {
    //         throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
    //     }
    // }

    /**
     * 减去库存
     *
     * @param carts key : skuId
     *              value: num
     */
    public void minusStock(Map<Long, Integer> carts) {
        for (Map.Entry<Long, Integer> entry : carts.entrySet()) {
            // 减去sku的id
            Long key = entry.getKey();
            // 减去多少量
            Integer value = entry.getValue();
            // 更新商品 (简单版)
            Sku record = new Sku();
            record.setId(key);
            Sku sku = skuMapper.selectByPrimaryKey(record);
            if (sku == null) {
                throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
            }
            record.setStock(sku.getStock() - value);
            int count = skuMapper.updateByPrimaryKeySelective(record);
            if (count != 1) {
                throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
            }

            // 问题1 - 优化：
            // 问题2 - 超卖问题：
        }
    }
}